package com.tp.server;

import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketException;
import java.util.ArrayList;
import java.util.List;

import org.jdom2.Document;
import org.jdom2.Element;
import org.jdom2.input.SAXBuilder;

/**
 * Instancia las peticiones de clientes
 * 
 * @author Franke Oviedo Triay
 * 
 *
 */
public class DnsResolver extends Thread {
	private static int port = 8085;

	private ServerSocket serverSocket = null;
	private Socket clientSocket = null;
	private boolean serverRunning;
	private int idSession = 0;
	private Registry dnsTree;
	private DomainCache domainCache;
	
	
	List<ClientThread> clients;

	/**
	 * Constructor.
	 */
	public DnsResolver() {
		try {
			dnsTree = loadDnsTree();
			domainCache = new DomainCache();
			serverSocket = new ServerSocket(port);
			serverRunning = true;
			//Mantiene a los clientes en una lista
			clients = new ArrayList<ClientThread>();
		} catch (IOException e) {
			System.err
					.println("could not get I/O from socketServer for this port: "
							+ port + "Exception" + e);
			System.exit(1);
		}
	}

	@Override
	public void run() {

		//Se pone a la escucha de una conexion a este socket.
		while (serverRunning) {

			try {

				clientSocket = serverSocket.accept();
				//Lanza un hilo por cada nuevo cliente y lo agrega a la lista de clientes.
				ClientThread clientThread = new ClientThread(clientSocket,
						idSession, dnsTree, domainCache);
				clientThread.start();
				// Agregar cliente a la lista de clientes
				clients.add(clientThread);
				idSession++;
			} catch (SocketException se) {
				System.out.println("Socket Closed ");
			} catch (IOException e) {
				System.err.println("accept fail from this port: " + port);
				System.exit(1);
			}
		}

	}

	/**
	 * Desconecta el servidor y mata los clientes.
	 */
	public void desconectar() {
		try {
			for (ClientThread client : clients) {
				if (!client.getSocket().isClosed()) {
					client.getSocket().close();
				}
			}
			serverSocket.close();
			serverRunning = false;
		} catch (IOException ex) {
			System.out.println("Error closing server: " + ex.getMessage());
		}
	}
	
	/**
	 * Carga el arbol de registros DNS a partir de un archivo XML
	 * @return Registry retorna el nodo raiz de los registros DNS.
	 */
	private Registry loadDnsTree(){
		SAXBuilder builder = new SAXBuilder();
		Registry registry = null;
		try {

			Document dnsTree = builder.build(DnsResolver.class.getResourceAsStream("/dnsDb.xml"));
			if (dnsTree.hasRootElement()) {
				registry = loadTree(dnsTree.getRootElement());

			}

		} catch (Exception e) {

			System.out.println(":: Error levantando base de datos ::");
		} 
		
		return registry;
	}
	
	/**
	 * Mapea los nodos del xml al modelo de registros.
	 * @param node
	 * @return Registry nodo raiz del arbol.
	 */
	private Registry loadTree(Element node) {
		Registry reg = new Registry();
		reg.type = node.getAttribute("tipo").getValue();
		reg.value = node.getAttribute("valor").getValue();

		List<Element> children = node.getChildren();
		if (children != null && children.size() > 0) {
			for (Element child : children) {

				reg.childrenRegistries.put(child.getName(), loadTree(child));

			}
		}

		return reg;

	}

}
